package com.bicycle.model;

import com.bicycle.entity.Point;
import lombok.Data;

import java.util.*;

@Data
public class Graph {

    /**
     * 使用1代表路通，0代表障碍物，2代表地铁站
     */
    private int[][] data;
    private int[][] visited;//记入每个节点访问的次数
    private Map<Point, String> names;//地铁站名称
    private Map<Point, Integer> bicycleNumber;//每个地铁站初始时刻自行车数量
    private Map<Point, List<Bicycle>> bicycles;//每个地铁站初始时刻放置的自行车
    private Lorry []lorries;
    private List<Bicycle> movingBicycles = new ArrayList<Bicycle>();//当自行车移动时加入此集合
    private Map<Point,Integer>lorryMap;//表示是否分配货车给某个地铁站

    public Graph(int[][] data) {
        this(data, null, null, null);
    }

    /**
     * 初始化地图
     *
     * @param data          地图数据，二维数组
     * @param names         地铁站名称
     * @param bicycleNumber 单车的数量
     * @param bicycles      单车初始化
     */
    public Graph(int[][] data, Map<Point, String> names, Map<Point, Integer> bicycleNumber, Map<Point, List<Bicycle>> bicycles) {
        this.data = data;
        this.visited = new int[data.length][data[0].length];
        for (int i = 0; i < data.length; i++) {

            for (int j = 0; j < data[i].length; j++) {
                visited[i][j] = 0;
            }
        }
        this.names = names;
        this.bicycleNumber = bicycleNumber;
        this.bicycles = bicycles;
    }

    public Graph(int[][] data, Map<Point, String> names, Map<Point, Integer> bicycleNumber, Map<Point, List<Bicycle>> bicycles,Lorry []lorries){
        this(data,names,bicycleNumber,bicycles);
        this.lorries = lorries;
    }

    public Graph(int[][] data, Map<Point, String> names, Map<Point, Integer> bicycleNumber, Map<Point, List<Bicycle>> bicycles,Lorry []lorries,Map<Point,Integer>lorryMap){
        this(data,names,bicycleNumber,bicycles,lorries);
        this.lorryMap=lorryMap;
    }

    /**
     * 搜索路径
     *
     * @param startPoint 开始节点
     * @param end        终点
     * @return 路线
     */
    public LinkedList<Point> searchRoute(Point startPoint, Point end) {

        LinkedList<Point> points = null;
        this.updateVisited();
        while (points == null || points.size() == 0) {
            points = this.search(startPoint, end);
            this.updateVisited();
        }
        return points;
    }

    /**
     * 搜索路径，从可能的路径当中随机选取一条
     * @param startPoint 开始节点
     * @param end        终点
     * @return 路线
     */
    private LinkedList<Point> search(Point startPoint, Point end) {
        //暂存节点
        LinkedList<Point> temp = new LinkedList<>();
        //单条路线
        LinkedList<Point> route = new LinkedList<>();
//        route.add(startPoint);
        //查看和startPoint相邻节点
        int row = startPoint.row;
        int column = startPoint.column;
        if (row - 1 >= 0 && data[row - 1][column] != 0 && visited[row - 1][column] == 0) {
            temp.add(new Point(row - 1, column));
        }
        if (row + 1 < data.length && data[row + 1][column] != 0 && visited[row + 1][column] == 0) {
            temp.add(new Point(row + 1, column));
        }
        if (column + 1 < data[column].length && data[row][column + 1] != 0 && visited[row][column + 1] == 0) {
            temp.add(new Point(row, column + 1));
        }
        if (column - 1 >= 0 && data[row][column - 1] != 0 && visited[row][column - 1] == 0) {
            temp.add(new Point(row, column - 1));
        }
        if (temp.size() == 0) {
            //startPoint没有相邻的节点
            return route;
        }
        //按顺序遍历节点
        for (int i = 0; i < temp.size(); i++) {
            //取出一个节点，加入路线类
            int index = new Random().nextInt(temp.size());
            route.add(temp.get(index));
            Point point = temp.get(index);
            this.visited[point.row][point.column] = 1;
            if (temp.get(index).row == end.row && temp.get(index).column == end.column) {
//                routes.add(route);
                return route;
            } else {
                //以此节点开始递归
                LinkedList<Point> remain = search(temp.get(index), end);
                if (remain.size() > 0 && remain.getLast().row == end.row && remain.getLast().column == end.column) {
                    route.addAll(remain);
                    return route;
                } else {
                    route.remove(temp.get(index));
                    this.visited[point.row][point.column] = 0;
                }
            }
        }
        return route;
    }

    /**
     * 更新visited访问数组
     */
    public void updateVisited() {
        for (int i = 0; i < data.length; i++) {
            for (int j = 0; j < data[i].length; j++) {
                visited[i][j] = 0;
            }
        }
    }

    /**
     * 检查每个地铁站的单车数量是否满足需要
     *
     * @return 单车少于8辆的地铁站
     */
    public List<Point> checkTheBicycles() {

        ArrayList<Point> points = new ArrayList<>();
        for (Point point : bicycles.keySet()) {

            if (bicycles.get(point).size() <= 30) {
                points.add(point);
            }
        }
        return points;
    }

    /**
     * 找到单车最多的地方
     *
     * @return 单车最多的地铁站坐标
     */
    public Point findTheMostBicycles() {

        int max = 0;
        Point maxPoint = null;
        for (Point point : bicycles.keySet()) {

            if (bicycles.get(point).size() > max) {
                max = bicycles.get(point).size();
                maxPoint = point;
            }
        }
        return maxPoint;
    }

    /**
     * 由地铁站随机生成乘客出发位置和终点位置
     *
     * @return 起始位置和终点位置
     */
    public Point[] randomPoints() {

        Object[] objects = bicycles.keySet().toArray();
        Random random = new Random();
        Point[] targetPoints = new Point[2];
        int index = random.nextInt(objects.length);
        targetPoints[0] = (Point) objects[index];
        targetPoints[1] = (Point) objects[(index + objects.length - 1) % objects.length];

        return targetPoints;
    }

    /**
     * 返回地铁站A和C
     * @return
     */
    public Point[] acSubwayTest(){
        Object[] objects = bicycles.keySet().toArray();
        Point[] targetPoints = new Point[2];
        targetPoints[0] = (Point) objects[0];
        targetPoints[1] = (Point) objects[1];
        return targetPoints;
    }
}

